Android CGroup实际情况调研
本文也发布在公司论坛中
内容概要:对于Android线程所属的CGroup,一些资料认为是通过线程优先级来进行划分的。但经实践,发现部分Android版本与该观点并不一致,那么实际情况又是怎样的呢?本篇文章就该问题进行了探讨。
一、CGroup简要介绍
在Linux中,不同线程分配cpu时间片的策略首先是基于线程优先级的,线程优先级越高,越容易分配到cpu。但是这样就产生了低优先级线程一直都被抢占cpu时间的问题,为解决该问题,Linux 2.6.23版本中引入了CFS策略,该策略不但会参考单个线程的优先级,还会追踪每个线程已经获取到的time slice数量,如果高优先级的线程已经执行了很长时间,但低优先级的线程一直在等待,后续系统会保证低优先级的线程也能获取更多的CPU时间。这就产生了新的问题:优先级高的线程并不一定总能在争取时间片上有绝对的优势,反映在Android中,就会出现UI线程被后台线程抢占cpu时间的问题。所以在Linux 2.6.24中又引入了CGroup的概念,让从属于特定CGroup的线程能够占据更多的时间片而不被低线程抢占,从而提升了总体效率[1]。
二、Android中不同版本所属CGroup调研
在Android中,存在两类特别重要的CGroup,一类是foreground group,UI线程属于这一类。另一类是background group,工作线程属于这一类。那么你可能要问,线程所属foreground group和background group到底怎样划分呢?查阅API文档以及相关资料[2],当我们使用setThreadPriority,nice值大于等于THREAD_PRIORITY_BACKGROUND将属于background group,其余属于foreground group。
但是,这种说法正确嘛?让我们来做个实验,首先通过以下代码设置线程优先级。
1 | private static final ThreadFactory sDBThreadFactory = new ThreadFactory() { |
在Android5.1.0中当线程优先级为Process.THREAD_PRIORITY_BACKGROUND的Async DB Thread所属的进程处于前台时,其cgroup却为fg,当所属进程进入到后台时,其cgroup为bg
而在Android 4.1中,无论是否处于前台,其cgroup都为bg
看来,实际情况是和版本有关,那么线程的cgroup是具体怎样设置的呢?查阅相关源代码[3],找到是在
sched_policy.c的get_sched_policy方法进行相关设置的,这里以Android4.1为例,其他版本逻辑基本一致,除了Android7.0[4],增加了一层宏定义判断#ifdef USE_CPUSETS
1 | int get_sched_policy(int tid, SchedPolicy *policy) |
上述代码基本逻辑概要如下:
该方法首先查看变量__sys_supports_schedgroups是否为true,
1 | if (!access("/dev/cpuctl/tasks", F_OK)) { |
也就是查看/dev/cpuctl/tasks是否存在:当/dev/cpuctl/tasks存在时,access返回0,满足条件__sys_supports_schedgroups为1。
那么Android5.1满足什么样的条件呢?实际上,在Android5.1的init.rc[5]中会创建了该tasks文件,但4.1.1[6]不存在该文件。那么5.1.0系统就使用getSchedulerGroup得到cgroup,也就是查找/proc/线程id/cgroup文件中的2:cpu:/一行的内容,
当整个进程在前台时,其2:cpu:的值为空,而处于后台时,则为bg_non_interactive。
而/proc/
接下来是Android4.1.1,他直接使用sched_getscheduler,也就是我们之前sched_setscheduler设置的policy:
1 | sched_setscheduler(tid, (policy == SP_BACKGROUND) ? SCHED_BATCH : SCHED_NORMAL, ¶m); |
而sched_setscheduler则是由set_sched_policy调用,
1 | if (gDoSchedulingGroup) { |
也就是通过线程的优先级来设置所属CGroup。
上面提到的/dev/cpuctl/tasks和在Android7.0中引入的/dev/cpuset/等文件的创建在init.rc中进行定义[5],不同的版本有不同的定义,这里查阅不同版本的源码,做一下Android不同版本的CGroup的实际情况的总结:
Android 4.0 Ice Cream Sandwich,根据线程优先级设置cgroup[9]
Android 4.1/4.2/4.3 Jelly Bean,根据线程优先级设置cgroup[6]
Android 4.4 KitKat,根据线程优先级设置cgroup[10]
Android 5.0/5.1 Lollipop,取所属进程的cgroup值[5]
Android 6.0 Marshmallow,取所属进程的cgroup值[11]
Android 7.0 Nougat,取所属进程的cgroup值[12]
参考资料
[1]http://coolshell.cn/articles/17049.html
[2]http://www.androiddesignpatterns.com/2014/01/thread-scheduling-in-android.html
[3]https://android.googlesource.com/platform/system/core/+/jb-release/libcutils/sched_policy.c
[4]https://android.googlesource.com/platform/system/core/+/nougat-release/libcutils/sched_policy.c
[5]https://android.googlesource.com/platform/system/core/+/lollipop-mr1-release/rootdir/init.rc
[6]https://android.googlesource.com/platform/system/core/+/jb-release/rootdir/init.rc
[7]https://github.com/torvalds/linux/blob/master/kernel/cgroup.c
[8]http://www.infoq.com/cn/articles/docker-kernel-knowledge-cgroups-resource-isolation
[9]https://android.googlesource.com/platform/system/core/+/ics-mr0-release/rootdir/init.rc
[10]https://android.googlesource.com/platform/system/core/+/kitkat-release/rootdir/init.rc
[11]https://android.googlesource.com/platform/system/core/+/marshmallow-release/rootdir/init.rc
[12]https://android.googlesource.com/platform/system/core/+/nougat-release/rootdir/init.rc